/*
 * Speck cipher implementation and testing code
 * (c) 2013 by Sebastian Gesemann
 *
 * Speck is a family of lightweight block ciphers designed by a team
 * of NSA members (see http://eprint.iacr.org/2013/404.pdf )
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef SG_INTEGER_HPP_INCLUDED
#define SG_INTEGER_HPP_INCLUDED

#include <climits>

/// This namespace contains some metaprogramming for selecting the
/// appropriate integer type for a given number of bits as well as
/// the cyclic shift (lcs,rcs) and modular add/sub operations.
namespace integer {

template<unsigned long long Number, unsigned Add = 0>
struct bits_needed : bits_needed<
	(Number>=128 ? Number>>8 : Number>>1),
	(Number>=128 ? 8 : (Number>0)) + Add
> {};
template<unsigned Add>
struct bits_needed<0,Add> { static const unsigned value = Add; };

template<class T, unsigned long long Max>
struct ty
{
	static const unsigned long long max = Max;
	static const unsigned bits = bits_needed<Max>::value;
	typedef T type;
};

template<bool B, class T, class U> struct select_super : U {};
template<class T, class U> struct select_super<true,T,U> : T {};

/// gives you an unsigned type with at least N bits
template<int N>
struct select_unsigned :
	select_super<(UCHAR_MAX  >> (N-1)), ty<unsigned char,UCHAR_MAX>,
	select_super<(USHRT_MAX  >> (N-1)), ty<unsigned short,USHRT_MAX>,
	select_super<(UINT_MAX   >> (N-1)), ty<unsigned int,UINT_MAX>,
	select_super<(ULONG_MAX  >> (N-1)), ty<unsigned long,ULONG_MAX>,
	select_super<(ULLONG_MAX >> (N-1)), ty<unsigned long long,ULLONG_MAX>,
	ty<void,0> > > > > >
{};

template<int N, int PadBits = select_unsigned<N>::bits - N>
struct ops
{
	typedef typename select_unsigned<N>::type word_type;
	static const word_type mask = word_type(-1) >> PadBits;

	static inline word_type lcs(word_type x, unsigned n)
	{ return ((x << n) & mask) | ((x & mask) >> (N-n)); }
	static inline word_type rcs(word_type x, unsigned n)
	{ return ((x << (N-n)) & mask) | ((x & mask) >> n); }
	static inline word_type add(word_type x, word_type y)
	{ return (x + y) & mask; }
	static inline word_type sub(word_type x, word_type y)
	{ return (x - y) & mask; }
};

template<int N> // In case word_type has EXACTLY N bits
struct ops<N,0> // we don't need to apply the mask.
{
	typedef typename select_unsigned<N>::type word_type;
	static const word_type mask = word_type(-1);

	static inline word_type lcs(word_type x, unsigned n)
	{ return (x << n) | (x >> (N-n)); }
	static inline word_type rcs(word_type x, unsigned n)
	{ return (x << (N-n)) | (x >> n); }
	static inline word_type add(word_type x, word_type y)
	{ return x + y; }
	static inline word_type sub(word_type x, word_type y)
	{ return x - y; }
};

} // namespace integer	
	
#endif
